﻿using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace FitAutomation.FitProcess
{
	class FitProcessListener
	{
		readonly TcpListener _listener;
		readonly Thread _thread;
		FitProcessHost _host;

		public FitProcessListener(int port, string mutexToRaise)
		{
			_listener = new TcpListener(IPAddress.Loopback, port);
			_listener.Start();
			using (var mx = new EventWaitHandle(false, EventResetMode.ManualReset, mutexToRaise))
			{
				mx.Set();
			}
			_thread = new Thread(Worker)
				{
					IsBackground = true,
					Name = "FitProcess Listener Worker. Port=" + port,
					Priority = ThreadPriority.BelowNormal
				};
			_thread.Start();
		}

		void Worker()
		{
			try
			{
				_host = new FitProcessHost();
				var client = _listener.AcceptTcpClient();

				while (true)
				{
					var buf = client.Receive();
					if (buf == null)
					{
						break;
					}
					Process(buf, client);
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.ToString());
			}
		}

		void Process(Message buf, TcpClient client)
		{
			MessageCode mc = buf;
			switch (mc)
			{
				case MessageCode.ErrorNotMessageCode:
					Process(buf.AsString, client);
					break;
				case MessageCode.CloseProcess:
					System.Diagnostics.Process.GetCurrentProcess().Kill();
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}
		}

		void Process(string command, TcpClient client)
		{
			var parts = command.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			if (parts.Length < 0)
			{
				throw new ArgumentOutOfRangeException("", "No command");
			}
			var cmd = parts[0].ToLowerInvariant();
			switch (cmd)
			{
				case "ref":
					var objectName = parts[1];
					var memberName = parts[2];
					object[] objArgs;
					if (parts.Length > 3)
					{
						objArgs = Serializer.DeserializePackage(parts[3]).ToArray();
					}
					else
					{
						objArgs = null;
					}
					var result = _host.PerformReflect(objectName, memberName, objArgs);
					if (result == null)
					{
						client.Send((Message)MessageCode.OK);
					}
					else
					{
						client.Send(Serializer.SerializePackage(result));
					}
					break;
				case "new":
					if (parts.Length != 3)
					{
						throw new ArgumentException("command 'new' should have 3 parts: 'new;objname;objtype'");
					}
					var typeName = parts[2];

					var type = Type.GetType(typeName, false, true);
					if (type == null)
					{
						throw new Exception();
					}
					_host.NewObject(parts[1], type);
					client.Send((Message)MessageCode.OK);
					break;
				default:
					client.Send((Message)"400 Unknown command");
					throw new ArgumentOutOfRangeException("", "Unknown command");
			}
		}
	}
}